"""passlib.utils._blowfish._gen_files - meta script that generates unrolled.py"""
#=============================================================================
# imports
#=============================================================================
# core
import os
import textwrap
# pkg
from passlib.utils.compat import irange
# local

#=============================================================================
# helpers
#=============================================================================
def varlist(name, count):
    return ", ".join(name + str(x) for x in irange(count))


def indent_block(block, padding):
    """ident block of text"""
    lines = block.split("\n")
    return "\n".join(
        padding + line if line else ""
        for line in lines
    )

BFSTR = """\
                ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) +
                  S3[l & 0xff]) & 0xffffffff)
""".strip()

def render_encipher(write, indent=0):
    for i in irange(0, 15, 2):
        write(indent, """\
            # Feistel substitution on left word (round %(i)d)
            r ^= %(left)s ^ p%(i1)d

            # Feistel substitution on right word (round %(i1)d)
            l ^= %(right)s ^ p%(i2)d
        """, i=i, i1=i+1, i2=i+2,
             left=BFSTR, right=BFSTR.replace("l","r"),
             )

def write_encipher_function(write, indent=0):
    write(indent, """\
        def encipher(self, l, r):
            \"""blowfish encipher a single 64-bit block encoded as two 32-bit ints\"""

            (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9,
              p10, p11, p12, p13, p14, p15, p16, p17) = self.P
            S0, S1, S2, S3 = self.S

            l ^= p0

            """)
    render_encipher(write, indent+1)

    write(indent+1, """\

        return r ^ p17, l

        """)

def write_expand_function(write, indent=0):
    write(indent, """\
        def expand(self, key_words):
            \"""unrolled version of blowfish key expansion\"""
            ##assert len(key_words) >= 18, "size of key_words must be >= 18"

            P, S = self.P, self.S
            S0, S1, S2, S3 = S

            #=============================================================
            # integrate key
            #=============================================================
        """)
    for i in irange(18):
        write(indent+1, """\
            p%(i)d = P[%(i)d] ^ key_words[%(i)d]
        """, i=i)
    write(indent+1, """\

        #=============================================================
        # update P
        #=============================================================

        #------------------------------------------------
        # update P[0] and P[1]
        #------------------------------------------------
        l, r = p0, 0

        """)

    render_encipher(write, indent+1)

    write(indent+1, """\

        p0, p1 = l, r = r ^ p17, l

        """)

    for i in irange(2, 18, 2):
        write(indent+1, """\
            #------------------------------------------------
            # update P[%(i)d] and P[%(i1)d]
            #------------------------------------------------
            l ^= p0

            """, i=i, i1=i+1)

        render_encipher(write, indent+1)

        write(indent+1, """\
            p%(i)d, p%(i1)d = l, r = r ^ p17, l

            """, i=i, i1=i+1)

    write(indent+1, """\

        #------------------------------------------------
        # save changes to original P array
        #------------------------------------------------
        P[:] = (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9,
          p10, p11, p12, p13, p14, p15, p16, p17)

        #=============================================================
        # update S
        #=============================================================

        for box in S:
            j = 0
            while j < 256:
                l ^= p0

        """)

    render_encipher(write, indent+3)

    write(indent+3, """\

                box[j], box[j+1] = l, r = r ^ p17, l
                j += 2
        """)

#=============================================================================
# main
#=============================================================================

def main():
    target = os.path.join(os.path.dirname(__file__), "unrolled.py")
    fh = file(target, "w")

    def write(indent, msg, **kwds):
        literal = kwds.pop("literal", False)
        if kwds:
            msg %= kwds
        if not literal:
            msg = textwrap.dedent(msg.rstrip(" "))
        if indent:
            msg = indent_block(msg, " " * (indent*4))
        fh.write(msg)

    write(0, """\
        \"""passlib.utils._blowfish.unrolled - unrolled loop implementation of bcrypt,
        autogenerated by _gen_files.py

        currently this override the encipher() and expand() methods
        with optimized versions, and leaves the other base.py methods alone.
        \"""
        #=================================================================
        # imports
        #=================================================================
        # pkg
        from passlib.utils._blowfish.base import BlowfishEngine as _BlowfishEngine
        # local
        __all__ = [
            "BlowfishEngine",
        ]
        #=================================================================
        #
        #=================================================================
        class BlowfishEngine(_BlowfishEngine):

        """)

    write_encipher_function(write, indent=1)
    write_expand_function(write, indent=1)

    write(0, """\
            #=================================================================
            # eoc
            #=================================================================

        #=================================================================
        # eof
        #=================================================================
        """)

if __name__ == "__main__":
    main()

#=============================================================================
# eof
#=============================================================================
